package md.sv.dbu.executor;

import md.sv.dbu.DBU;
import md.sv.dbu.config.jaxb.DbuType;
import md.sv.dbu.config.jaxb.ReceiveMQType;
import md.sv.dbu.config.jaxb.ReceiveTaskType;
import md.sv.dbu.config.jaxb.SqlConfigType;
import md.sv.dbu.receiver.MessageReceiver;
import md.sv.dbu.utils.DbuUtils;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author MG
 * @since 16.01.2014
 */
public class MQReceiveExecutor extends ReceiveExecutor<ReceiveMQType> {
    private static final Logger logger = LoggerFactory.getLogger(ReceiveExecutor.class);

    private String errorFilePattern;

    public MQReceiveExecutor(MessageReceiver<ReceiveMQType> messageReceiver) {
        super(messageReceiver, ReceiveMQType.class);
    }

    @Override
    public void executeType(final ReceiveMQType receiveMQ) {
            String newIdVariableName = receiveMQ.getNewIdVariableName();
            dbuExecutor.getVariables().put(newIdVariableName, dbuExecutor.generateNewGUID());

            errorFilePattern = receiveMQ.getErrorPatternFileName();

            String errorTemplateFile = null;

            if (receiveMQ.getQueueResult() != null) {
                if (!dbuExecutor.getMessageSender().connect(receiveMQ.getManager(), receiveMQ.getQueueResult(), receiveMQ.isByteMessage())) {
                    logger.error("Could not connect to Queue [{}] for the MQ Manager [{}]", receiveMQ.getManager(), receiveMQ.getQueueResult());
                    return;
                }
            }

            // load result error template xml
            if (receiveMQ.getResultErrorTemplateFile() != null) {
                String resultTemplatePath = FilenameUtils.concat(dbuExecutor.getRootPath(), receiveMQ.getResultErrorTemplateFile());
                if (!DbuUtils.fileExists(resultTemplatePath)) {
                    logger.error("ReceiveMQ has invalid result error template file: {}", resultTemplatePath);
                    return;
                }
                errorTemplateFile = DbuUtils.readFile(resultTemplatePath);
                if (errorTemplateFile == null) {
                    logger.error("Result error template file [{}] could not be read for ReceiveMQ", resultTemplatePath);
                    return;
                }
            }

            final List<ReceiveTask> receiveTasks = new ArrayList<ReceiveTask>();

            for (ReceiveTaskType receiveTaskType : receiveMQ.getReceiveTask()) {
                ReceiveTask receiveTask = new ReceiveTask();
                receiveTask.receiveTaskType = receiveTaskType;

                if (!receiveTask.receiveTaskType.isExecute()) {
                    continue;
                }

                // parse config file
                String configPath = FilenameUtils.concat(dbuExecutor.getRootPath(), receiveTask.receiveTaskType.getConfigFile());
                if (!DbuUtils.fileExists(configPath)) {
                    logger.error("Config File [{}] does not exist or is not readable.", configPath);
                    continue;
                }
                DbuType configDbu;
                try {
                    configDbu = dbuExecutor.getXmlParser().parseConfigFile(DbuUtils.openFile(configPath), DbuType.class, DBU.class.getResourceAsStream("configXMD.xsd"));
                } catch (Exception e) {
                    logger.error("Could not parse config file [{}]", configPath);
                    logger.error("Underlying error: ", e);
                    continue;
                }

                if (configDbu.getConfigurationOrUpdateTaskOrReceiveMQ().size() == 0 ||
                        !(configDbu.getConfigurationOrUpdateTaskOrReceiveMQ().get(0) instanceof SqlConfigType)) {
                    logger.error("Config File {} is not an SQL Config", configPath);
                    continue;
                }
                receiveTask.sqlConfig = (SqlConfigType) configDbu.getConfigurationOrUpdateTaskOrReceiveMQ().get(0);

                // load validation xsd
                String[] validatePaths = receiveTask.receiveTaskType.getValidateFile().split(" ");
                receiveTask.validateFiles = new String[validatePaths.length];
                for (int i = 0; i < validatePaths.length; i++) {
                    String validatePath = FilenameUtils.concat(dbuExecutor.getRootPath(), validatePaths[i]);
                    if (!DbuUtils.fileExists(validatePath)) {
                        logger.error("Receive task [{}] has invalid validation file: {}", receiveTask.receiveTaskType.getDescription(), validatePath);
                        continue;
                    }
                    receiveTask.validateFiles[i] = DbuUtils.readFile(validatePath);
                    if (receiveTask.validateFiles[i] == null) {
                        logger.error("Validation file [{}] could not be read for task [{}]", validatePath, receiveTask.receiveTaskType.getDescription());
                        continue;
                    }

                }

                // load template xml
                String templatePath = FilenameUtils.concat(dbuExecutor.getRootPath(), receiveTask.receiveTaskType.getTemplateFile());
                if (!DbuUtils.fileExists(templatePath)) {
                    logger.error("Receive task [{}] has invalid template file: {}", receiveTask.receiveTaskType.getDescription(), templatePath);
                    continue;
                }
                receiveTask.templateFile = DbuUtils.readFile(templatePath);
                if (receiveTask.templateFile == null) {
                    logger.error("Template file [{}] could not be read for task [{}]", templatePath, receiveTask.receiveTaskType.getDescription());
                    continue;
                }

                // load result success template xml
                if (receiveTask.receiveTaskType.getResultSuccessTemplateFile() != null) {
                    String resultTemplatePath = FilenameUtils.concat(dbuExecutor.getRootPath(), receiveTask.receiveTaskType.getResultSuccessTemplateFile());
                    if (!DbuUtils.fileExists(resultTemplatePath)) {
                        logger.error("Receive task [{}] has invalid result success template file: {}", receiveTask.receiveTaskType.getDescription(), resultTemplatePath);
                        continue;
                    }
                    receiveTask.resultSuccessTemplateFile = DbuUtils.readFile(resultTemplatePath);
                    if (receiveTask.templateFile == null) {
                        logger.error("Result success template file [{}] could not be read for task [{}]", resultTemplatePath, receiveTask.receiveTaskType.getDescription());
                        continue;
                    }
                }

                // if all OK, then add this receive task to the task list
                receiveTasks.add(receiveTask);
            }

            // execute all valid receive tasks
            if (receiveTasks.size() > 0) {
                final String finalErrorTemplateFile = errorTemplateFile;
                messageReceiver.receiveMessages(receiveMQ, receiveMQ.getMaxMessageRead(), new MessageReceiver.ReceiverCallback() {
                    @Override
                    public boolean onReceive(String message) {
                        boolean result = executeReceiveTasks(message, receiveTasks);
                        if (result) {
                            dbuExecutor.commitExecutors();
                        } else {
                            dbuExecutor.rollbackExecutors();

                            if (finalErrorTemplateFile != null) {
                                final Map<String, List<Object>> templateXmlValues = new HashMap<String, List<Object>>();
                                String docIdVar = receiveMQ.getDocumentIdVar();
                                String docIdTag = receiveMQ.getDocumentIdTag();
                                if (templateXmlValues.get(docIdVar) == null) {
                                    templateXmlValues.put(docIdVar, new ArrayList<Object>());
                                }
                                templateXmlValues.get(docIdVar).add(getDocumentId(message, docIdTag));
                                sendMQResult(finalErrorTemplateFile, templateXmlValues);
                            }
                        }
                        return result;
                    }
                });
            }

            if (receiveMQ.getQueueResult() != null) {
                dbuExecutor.getMessageSender().close();
            }
    }

    @Override
    public void onMessageNotProcessed(String mqMessage) {
        logger.warn("Mq Message could not be processed.");
        StringBuilder logFileName = new StringBuilder(errorFilePattern);
        dbuExecutor.getGlobalVariablesReplacer().replaceVariables(logFileName, dbuExecutor.getVariables(), dbuExecutor.getDateTimeFormats());
        String logFilePath = FilenameUtils.concat(dbuExecutor.getRootPath(), logFileName.toString());
        logger.debug("Saving Error XML file to [{}]", logFilePath);
        DbuUtils.writeFile(logFilePath, mqMessage);
    }
}
